package com.peas.platform.common.core.controller;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.peas.platform.common.core.configuration.RequestContext;
import com.peas.platform.common.core.configuration.RequestContextUtil;
import com.peas.platform.common.core.constant.Constants;
import com.peas.platform.common.core.controller.vo.GarbageCollectorInfo;
import com.peas.platform.common.core.controller.vo.MemoryInfo;
import com.peas.platform.common.core.dto.ResponseDto;
import com.peas.platform.common.core.enums.BaseEnum;
import com.peas.platform.common.core.property.ProjectProperties;
import com.peas.platform.common.core.utils.ExcelTool;
import com.peas.platform.common.core.utils.RequestUtil;
import com.peas.platform.common.core.utils.SystemTool;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.SneakyThrows;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.context.ApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@RestController
@RequestMapping("/api/v1/common")
@Api(tags = {"API接口[通用]"})
@CrossOrigin
public class CommonController {

    @Value("${spring.application.name}")
    private String springApplicationName;
    @Value("${spring.profiles.active}")
    private String profiles;

    @Resource
    private ApplicationContext ac;
    @Resource
    private ProjectProperties projectProperties;
    @Resource
    private SystemTool systemTool;

    @ApiOperation("查询服务节点主机名")
    @GetMapping(value = "/hostname")
    public ResponseDto<String> hostname() {
        return ResponseDto.getSuccessResponseDto(System.getenv("HOSTNAME"));
    }

    @ApiOperation("查询服务节点构建时间")
    @GetMapping(value = "/build")
    public ResponseDto<String> build() {
        return ResponseDto.getSuccessResponseDto(projectProperties.getBuild());
    }

    @ApiOperation("查询服务节点版本")
    @GetMapping(value = "/version")
    public ResponseDto<String> version() {
        return ResponseDto.getSuccessResponseDto(projectProperties.getVersion());
    }

    @ApiOperation("查询服务节点启动时间")
    @GetMapping(value = "/startup")
    public ResponseDto<String> startup() {
        return ResponseDto.getSuccessResponseDto(DateUtil.format(new Date(ac.getStartupDate()), DatePattern.NORM_DATETIME_FORMAT));
    }

    @ApiOperation("查询服务节点环境")
    @GetMapping(value = "/profiles")
    public ResponseDto<List<String>> profiles() {
        return ResponseDto.getSuccessResponseDto(Arrays.asList(ac.getEnvironment().getActiveProfiles()));
    }

    @ApiOperation("查询服务节点日期")
    @GetMapping(value = "/date")
    public ResponseDto<String> date() {
        return ResponseDto.getSuccessResponseDto(DateUtil.format(DateUtil.date(), DatePattern.NORM_DATE_PATTERN));
    }

    @ApiOperation("查询服务节点日期")
    @GetMapping(value = "/date/{add}")
    public ResponseDto<String> date(@PathVariable("add") Integer add) {
        if (add == null) {
            add = 0;
        }
        String result = DateUtil.format(DateUtil.offsetDay(DateUtil.date(), add), DatePattern.NORM_DATE_PATTERN);
        return ResponseDto.getSuccessResponseDto(result);
    }

    @ApiOperation("查询服务节点时间")
    @GetMapping(value = "/time")
    public ResponseDto<String> time() {
        return ResponseDto.getSuccessResponseDto(DateUtil.format(DateUtil.date(), DatePattern.NORM_TIME_PATTERN));
    }

    @ApiOperation("查询服务节点时间")
    @GetMapping(value = "/time/{add}")
    public ResponseDto<String> time(@PathVariable("add") Integer add) {
        if (add == null) {
            add = 0;
        }
        String result = DateUtil.format(DateUtil.offsetSecond(DateUtil.date(), add), DatePattern.NORM_DATE_PATTERN);
        return ResponseDto.getSuccessResponseDto(result);
    }

    @ApiOperation("查询服务节点日期时间")
    @GetMapping(value = "/datetime")
    public ResponseDto<String> datetime() {
        return ResponseDto.getSuccessResponseDto(DateUtil.format(DateUtil.date(), DatePattern.NORM_DATETIME_FORMAT));
    }

    @ApiOperation("查询服务节点配置")
    @GetMapping(value = "/config/{key}")
    public ResponseDto<String> config(@PathVariable("key") String key) {
        if (StrUtil.isEmpty(key)) {
            return ResponseDto.getSuccessResponseDto(null);
        }
        String value = ac.getEnvironment().getProperty(key);
        if (StrUtil.isNotEmpty(value)) {
            switch (key.toLowerCase().trim()) {
                case "encrypt.key":
                case "spring.cloud.config.server.git.password":
                case "spring.datasource.password":
                case "spring.redis.password":
                case "spring.rabbitmq.password":
                case "spring.elasticsearch.jest.password":
                case "spring.elasticsearch.rest.password":
                case "flowable.password":
                case "minio.secretkey":
                case "app.key":
                case "app.callnumkey":
                    value = "******";
            }
        }
        return ResponseDto.getSuccessResponseDto(value);
    }

    @ApiOperation("查询服务节点Bean定义")
    @GetMapping(value = "/beanDefinitionNames")
    public ResponseDto<List<String>> beanDefinitionNames() {
        String[] strs = ac.getBeanDefinitionNames();
        return ResponseDto.getSuccessResponseDto(Arrays.asList(strs));
    }

    @ApiOperation("查询服务节点Bean的信息")
    @GetMapping(value = "/bean/{name}")
    public ResponseDto<String> bean(@PathVariable("name") String name) {
        Object o = ac.getBean(name);
        return ResponseDto.getSuccessResponseDto(o.getClass().getName());
    }

    @ApiOperation("查询服务节点网卡信息")
    @SneakyThrows
    @GetMapping(value = "/networkInterface")
    public ResponseDto<List<String>> networkInterface() {
        List<String> nics = new ArrayList<>();
        for (Enumeration<NetworkInterface> nic = NetworkInterface.getNetworkInterfaces(); nic.hasMoreElements(); ) {
            NetworkInterface ifc = nic.nextElement();
            nics.add(ifc.getIndex() + "," + ifc.getName() + "," + ifc.getDisplayName() + "," + ifc.getInterfaceAddresses());
        }
        return ResponseDto.getSuccessResponseDto(nics);
    }

    @ApiOperation("查询服务节点编码器")
    @GetMapping(value = "/httpMessageConverters")
    public ResponseDto<List<String>> httpMessageConverters() {
        List<String> hmcs = new ArrayList<>();
        for (HttpMessageConverter<?> bean : ac.getBean(HttpMessageConverters.class).getConverters()) {
            hmcs.add(bean.getClass().getName());
        }
        return ResponseDto.getSuccessResponseDto(hmcs);
    }

    @SneakyThrows
    private static String getIp(Environment env) {
        Assert.notNull(env, "参数错误");
        String ip = env.getProperty("eureka.instance.ip-address");
        if (StrUtil.isEmpty(ip)) {
            ip = env.getProperty("spring.cloud.client.ip-address");
        }
        InetAddress address = InetAddress.getLocalHost();
        if (StrUtil.isEmpty(ip)) {
            ip = address.getHostAddress();
        }
        Assert.notBlank(ip, "获取本机IP失败");
        return ip;
    }

    private static Integer getPort(Environment env) {
        Assert.notNull(env, "参数错误");
        Integer port = Integer.valueOf(Objects.requireNonNull(env.getProperty("server.port")));
        Assert.notNull(port, "配置server.port不存在");
        return port;
    }

    @ApiOperation("查询服务节点信息")
    @GetMapping(value = "/info")
    public ResponseDto<JSONObject> info() {
        Environment env = ac.getEnvironment();
        JSONObject data = systemTool.buildJSONObject();
        data.set("id", ac.getId());
        data.set("applicationName", springApplicationName);
        data.set("build", projectProperties.getBuild());
        data.set("startup", DateUtil.format(new Date(ac.getStartupDate()), DatePattern.NORM_DATETIME_FORMAT));
        data.set("version", projectProperties.getVersion());
        data.set("hostname", env.getProperty("HOSTNAME", ""));
        data.set("namespace", env.getProperty("K8S_POD_NAMESPACE", ""));
        data.set("serviceAccount", env.getProperty("K8S_POD_SERVICE_ACCOUNT", ""));
        data.set("works", "");
        data.set("ip", getIp(env));
        data.set("port", getPort(env));
        data.set("profiles", Arrays.asList(env.getActiveProfiles()));
        data.set("profiles active", profiles);
        data.set("eip", HttpUtil.get("https://myip.ipip.net"));
        data.set("doc", projectProperties.getService() + "/doc.html");
        return ResponseDto.getSuccessResponseDto(data);
    }

    @ApiOperation(value = "查询系统属性")
    @GetMapping("/properties")
    public ResponseDto<JSONObject> properties() {
        Properties p = System.getProperties();
        JSONObject o = new JSONObject(p);
        return ResponseDto.getSuccessResponseDto(o);
    }

    @ApiOperation("加密字符串")
    @SneakyThrows
    @GetMapping("/encoder")
    public ResponseDto<String> encoder(String str, String charset) {
        Pattern p = Pattern.compile("[\u4e00-\u9fa5]+");
        Matcher m = p.matcher(str);
        StringBuffer b = new StringBuffer();
        while (m.find()) {
            m.appendReplacement(b, URLEncoder.encode(m.group(0), charset));
        }
        m.appendTail(b);
        return ResponseDto.getSuccessResponseDto(b.toString());
    }

    @ApiOperation("解密字符串")
    @SneakyThrows
    @GetMapping("/decoder")
    public ResponseDto<String> decoder(String str, String charset) {
        return ResponseDto.getSuccessResponseDto(URLEncoder.encode(str, charset));
    }

    @PostConstruct
    @SuppressWarnings("unchecked")
    public synchronized void initEnums() {
        if (Constants.enums.isEmpty()) {
            Set<Class<?>> classes = ClassUtil.scanPackageBySuper(Constants.BASE_PACKAGE, BaseEnum.class);
            for (Class<?> cls : classes) {
                if (cls.isEnum()) {
                    Constants.enums.put(cls.getSimpleName(), (Class<? extends BaseEnum>) cls);
                }
            }
        }
    }

    @ApiOperation(value = "查询枚举列表")
    @SneakyThrows
    @GetMapping("/enums")
    public ResponseDto<List<Map<String, String>>> enumsList(String type) {
        List<Map<String, String>> list;
        if (StrUtil.isBlank(type)) {
            return ResponseDto.getSuccessResponseDto(null);
        }
        if (Constants.enums.get(type) == null) {
            return ResponseDto.getSuccessResponseDto(null);
        } else {
            Class<? extends BaseEnum> clazz = Constants.enums.get(type);
            list = BaseEnum.getMapList(clazz);
            return ResponseDto.getSuccessResponseDto(list);
        }
    }

    @ApiOperation(value = "查询枚举列表（多个type用半角逗号分隔）")
    @SneakyThrows
    @GetMapping("/enums/multi")
    public ResponseDto<Map<String, List<Map<String, String>>>> enumsMulti(String types) {
        Map<String, List<Map<String, String>>> map = new HashMap<>();
        if (StrUtil.isBlank(types)) {
            for (Map.Entry<String, Class<? extends BaseEnum>> entry : Constants.enums.entrySet()) {
                String type = entry.getKey();
                Class<? extends BaseEnum> clazz = Constants.enums.get(type);
                List<Map<String, String>> list = BaseEnum.getMapList(clazz);
                map.put(type, list);
            }
        }
        if (StrUtil.isNotBlank(types)) {
            for (String type : types.split(StrUtil.COMMA)) {
                List<Map<String, String>> list = new ArrayList<>();
                if (Constants.enums.get(type) != null) {
                    Class<? extends BaseEnum> clazz = Constants.enums.get(type);
                    list = BaseEnum.getMapList(clazz);
                }
                map.put(type, list);
            }
        }
        return ResponseDto.getSuccessResponseDto(map);
    }

    @ApiOperation(value = "导出所有枚举")
    @SneakyThrows
    @GetMapping("/enums/download")
    public void enumsDownload(HttpServletRequest request, HttpServletResponse response) {
        assert request != null;
        String name = URLEncoder.encode("枚举类型.xlsx", StandardCharsets.UTF_8.name());
        response.setHeader("Content-disposition", "attachment; filename=\"" + name + "\"");
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.setContentType("application/vnd.ms-excel;charset=utf-8");
        List<List<?>> data = new ArrayList<>();
        List<String> head = Arrays.asList("枚举", "说明", "存储值", "显示值");
        for (Map.Entry<String, Class<? extends BaseEnum>> entry : Constants.enums.entrySet()) {
            String str = entry.getKey();
            Class<? extends BaseEnum> clazz = entry.getValue();
            for (BaseEnum e : clazz.getEnumConstants()) {
                String key = e.getKey();
                String value = e.getValue();
                List<String> row = CollUtil.newArrayList(str, e.comment(), key, value);
                data.add(row);
            }
        }
        try (ExcelWriter writer = ExcelTool.genExcelWriter(head, data)) {
            writer.flush(response.getOutputStream(), true);
        }
    }

    @ApiOperation(value = "查询日志级别")
    @GetMapping("/logger")
    public ResponseDto<JSONObject> loggerLevelChange() {
        JSONObject map = new JSONObject();
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger logger = loggerContext.getLogger("ROOT");
        map.set("level", logger.getLevel().levelStr);
        map.set("hostname", System.getenv("HOSTNAME"));
        return ResponseDto.getSuccessResponseDto(map);
    }

    @SneakyThrows
    @ApiOperation(value = "设置日志级别")
    @PostMapping("/logger/{level}")
    public ResponseDto<JSONObject> loggerLevelChange(@PathVariable(value = "level") String level) {
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        Logger logger = loggerContext.getLogger("ROOT");
        if (StrUtil.isBlank(level)) {
            level = "info";
        }
        level = level.trim().toLowerCase();
        if (StrUtil.equalsIgnoreCase(level, logger.getLevel().levelStr)) {
            JSONObject map = new JSONObject();
            map.set("level", logger.getLevel().levelStr);
            map.set("hostname", System.getenv("HOSTNAME"));
            return ResponseDto.getSuccessResponseDto(map);
        }
        switch (level) {
            case "trace":
                logger.setLevel(Level.TRACE);
                break;
            case "debug":
                logger.setLevel(Level.DEBUG);
                break;
            case "info":
                logger.setLevel(Level.INFO);
                break;
            case "warn":
                logger.setLevel(Level.WARN);
                break;
            case "error":
                logger.setLevel(Level.ERROR);
                break;
            default:
                break;
        }
        if (!StrUtil.equalsIgnoreCase(level, "info")) {
            final RequestContext rc = RequestContextUtil.getRequestContext();
            new Thread(() -> {
                RequestUtil.initRequestId(rc);
                ThreadUtil.sleep(5, TimeUnit.MINUTES);
                Logger log = loggerContext.getLogger("ROOT");
                log.setLevel(Level.INFO);
            }).start();
        }
        JSONObject map = new JSONObject();
        map.set("level", logger.getLevel().levelStr);
        map.set("hostname", System.getenv("HOSTNAME"));
        String dicovery = ac.getEnvironment().getProperty("spring.cloud.nacos.discovery.enabled");
        if (!StrUtil.equalsIgnoreCase("false", dicovery)) {
            NacosServiceManager nacosServiceManager = ac.getBean(NacosServiceManager.class);
            List<Instance> list = nacosServiceManager.getNamingService().getAllInstances(springApplicationName);
            if (CollUtil.isNotEmpty(list)) {
                for (Instance instance : list) {
                    String ip = instance.getIp();
                    int port = instance.getPort();
                    if (StrUtil.equalsIgnoreCase(ip, getIp(ac.getEnvironment()))) {
                        continue;
                    }
                    String url = "http" + "://" + ip + ":" + port + "/api/v1/common/logger/" + level;
                    HttpUtil.post(url, StrUtil.EMPTY);
                }
            }
        }
        return ResponseDto.getSuccessResponseDto(map);
    }

    @ApiOperation(value = "查询所有线程信息")
    @GetMapping("/monitor/allThreads")
    public ResponseDto<ThreadInfo[]> monitorAllThreads() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        long[] ids = threadBean.getAllThreadIds();
        return ResponseDto.getSuccessResponseDto(threadBean.getThreadInfo(ids));
    }

    @ApiOperation(value = "查询线程ID数组")
    @GetMapping("/monitor/threads")
    public ResponseDto<long[]> monitorThreads() {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        return ResponseDto.getSuccessResponseDto(threadBean.getAllThreadIds());
    }

    @ApiOperation(value = "查询线程信息")
    @GetMapping("/monitor/threads/{threadId}")
    public ResponseDto<ThreadInfo> monitorThread(@PathVariable(value = "threadId") Long threadId) {
        ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
        return ResponseDto.getSuccessResponseDto(threadBean.getThreadInfo(threadId));
    }

    @ApiOperation(value = "查询内存信息")
    @GetMapping("/monitor/memory")
    public ResponseDto<MemoryInfo> monitorMemory() {
        return ResponseDto.getSuccessResponseDto(new MemoryInfo());
    }

    @ApiOperation(value = "查询GC信息")
    @GetMapping("/monitor/gc")
    public ResponseDto<GarbageCollectorInfo> monitorGarbageCollector() {
        return ResponseDto.getSuccessResponseDto(new GarbageCollectorInfo());
    }

    @ApiOperation(value = "查询类加载器目录")
    @GetMapping("/getClassLoaders")
    public ResponseDto<JSONObject> getClassLoaders() {
        // Launcher launcher;
        String pathBoot = System.getProperty("sun.boot.class.path");
        String pathExt = System.getProperty("java.ext.dirs");
        String pathApp = System.getProperty("java.class.path");
        JSONObject o = new JSONObject();
        o.set("pathBoot", StrUtil.split(pathBoot, ";"));
        o.set("pathExt", StrUtil.split(pathExt, ";"));
        o.set("pathApp", StrUtil.split(pathApp, ";"));
        return ResponseDto.getSuccessResponseDto(o);
    }

}
